﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Reflection;
using System.Windows.Forms;
using Microsoft.Win32;

namespace ALDIOrganizer
{
    /// <summary>
    /// Keeps track of most recently used files, saving their list in Windows registry.    
    /// </summary>
    /// <remarks>
    /// It is used twice in the application - for keeping track of
    /// </remarks>
    public class MRUTracker
    {
        private readonly string _identifier;
        private readonly List<string> _list;
        /// <summary>
        /// Gets the read-only list of most recently used files.
        /// </summary>
        /// <remarks>
        /// To be used as datasource for listboxes, menus etc.
        /// </remarks>
        internal List<string> GetList
        {
            get
            {
                return _list;
            }
        }
        private readonly int _maximumFiles;
        
        private bool ListNearlyFull
        {
            get
            {
                return (_list.Count + 1 > _maximumFiles);
            }
        }
        
        private string _path
        {
            get
            {
                return String.Concat(@"Software\", // hard-coded, as Windows registry is not localized (I think!)
                    Application.CompanyName.Trim(),
                    @"\",
                    Application.ProductName.Trim(),
                    _identifier);
            }
        }
        private static string FileCode(int pIndex)
        {
            return String.Concat(
                Settings.Default.REGISTRY_FilePrefix,
                pIndex.ToString("00"));
        }

        internal delegate void OnListUpdatedEventHandler();
        
        /// <summary>
        /// Fires whenever the list is updated.
        /// </summary>
        /// <remarks>
        /// 1. Used by MainForm to repopulate "Recently opened files..." submenu When needed.
        /// 2. Not obligatory (the class checks whether it'pShift not null before it triggers it)
        /// </remarks>
        internal event OnListUpdatedEventHandler OnListUpdated;

        public MRUTracker(
            string pIdentifier,
            int pMaximumFiles)
        {
            _list = new List<string>();
            _identifier = pIdentifier;
            _maximumFiles = pMaximumFiles;
            LoadList();
        }

        /// <summary>
        /// Removes the last entry. Used When the list is about to exceed the size limit.
        /// </summary>
        private void DeleteLast()
        {
            if (_list.Count < 1)
            {
                return;
            }
            _list.RemoveAt(_list.Count - 1);
        }

        internal void ClearList(bool pAndSave)
        {
            _list.Clear();
            if (OnListUpdated != null)
            {
                OnListUpdated();
            }
            if (pAndSave)
            {
                SaveList();
            }
        }
        
        internal void AddEntry(string pFileName,
            bool pAtTheEnd, bool pAndSave)
        {
            if (_list.Contains(pFileName))
            {
                RemoveEntry(pFileName, pAndSave);
            }
            if (ListNearlyFull)
            {
                DeleteLast();
            }
            if (pAtTheEnd)
            {
                _list.Add(pFileName);
            }
            else
            {
                _list.Insert(0, pFileName);
            }
            if (OnListUpdated != null)
            {
                OnListUpdated();
            }
            if (pAndSave)
            {
                SaveList();
            }
        }

        internal void RemoveEntry(string pFileName, bool pAndSave)
        {
            var updated = false;
            /* in case if there are multiple identical entries, which in principle
             * should not happen, but... */
            while (_list.Contains(pFileName))
            {
                _list.Remove(pFileName);
                updated = true;
            }
            if (updated & (OnListUpdated != null))
            {
                OnListUpdated();
            }
            if (pAndSave)
            {
                SaveList();
            }
        }

        internal void LoadList()
        {
            // if the registry key does not existss, we assume the application was launched for the first time
            var firstTime = (Registry.CurrentUser.OpenSubKey(_path) == null);
            _list.Clear();
            if (firstTime)
            {
                var sample = String.Empty;
                if (_identifier == Settings.Default.REGISTRY_MRU_SQL_Suffix)
                {
                    sample = SQLiteAccess.GetFullDefaultPathAndFileName;
                }
                if (_identifier == Settings.Default.REGISTRY_MRU_Rotas_Suffix)
                {
                    sample = Program.GetFullDefaultPathAndFileName;
                }
                if (sample == String.Empty)
                // _identifier value could be wrong, or setting could be blank (thereby disabling the option)
                {
                    return;
                }
                Program.LogError(sample, null);
                if (!File.Exists(sample))
                {                    
                    return;
                }
                _list.Add(sample);
                SaveList();                
                return; /* and why do we not re-read our list at this point??
                         * well: what for?
                         * the _list variable already contains the entry we wanted there... */
            }

            // so it wasn't the first time: our registry key is in place. OK we'll retrieve it again...
            var key = Registry.CurrentUser.OpenSubKey(_path);
            // and populate our list.
            for (var i = 0; i < _maximumFiles; i++)
            {
                var name = FileCode(i);
                var value = (string)key.GetValue(
                    name,
                    String.Empty);
                if (String.IsNullOrEmpty(value) |
                    !File.Exists(value))
                {
                    break;
                }
                AddEntry(value, true, false);
            }
        }

        /// <summary>
        /// Writes the list into the registry
        /// </summary>
        /// <remarks>
        /// Deletes all the old values first, then writes the new ones.
        /// </remarks>
        internal void SaveList()
        {
            var key = Registry.CurrentUser.CreateSubKey(_path);
            if (key == null)
            {
                return;
            }
            for (var i = 0; i < _maximumFiles; i++)
            {
                key.DeleteValue(
                    FileCode(i),
                    false);
            }
            for (var i = 0; i < _list.Count; i++)
            {
                key.SetValue(
                    FileCode(i),
                    _list[i]);
            }
        }

        public static void ClearRegistryLeftovers()
        {
            try
            {
                Registry.CurrentUser.DeleteSubKeyTree(
                    @"Software\" + // hard-coded, as Windows registry is not localized (I think!)
                    Application.CompanyName.Trim());
            }
            catch { } // if it does not exist
        }

        
    }
}
